#pragma once
#include <iostream>
#include <unistd.h>
#include <string>
#include <cstring>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "nocopy.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"

using namespace log_ns;

static const int gsockfd = -1;
static const uint16_t glocalport = 8888;

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR
};

using func_t = std::function<std::string(std::string)>;
// UdpServer user("192.1.1.1", 8899)
// 一般服务器主要是用来进行网络数据读取和写入的, IO的
// 服务器IO逻辑 和 业务逻辑 解耦

class UdpServer : public nocopy
{
public:
    UdpServer(func_t func, uint16_t localport = glocalport)
        : _func(func),
          _sockfd(gsockfd),
          _localport(localport),
          _isrunning(false)
    {
    }
    void InitServer()
    {
        // 1. 创建socket文件
        _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(FATAL, "socket error\n");
            exit(SOCKET_ERROR);
        }
        LOG(DEBUG, "socket create success, _sockfd : %d", _sockfd); // 3

        // 2. bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_localport);
        local.sin_addr.s_addr = INADDR_ANY; // 服务器端, 进行任意 IP 地址绑定

        int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind error");
            exit(BIND_ERROR);
        }
        LOG(DEBUG, "socket bind success\n");
    }

    // inbuffer[n] = 0; 解释
    // inbuffer 是一个字符数组，用于存储接收到的数据。
    // n 是 recvfrom 返回的字节数，表示从套接字中成功接收到的数据的大小。
    // inbuffer[n] = 0; 这行代码的目的是将接收到的数据末尾标记为字符串的结束位置。
    // 在 C/C++ 中，0 字符（即 '\0'）是字符串的结束标志，它告诉程序在处理字符串时，遇到 '\0' 就停止。
    // 假设 n 是 10，那么 inbuffer[10] 会被设置为 0，这样 inbuffer 中的有效数据部分（从 inbuffer[0] 到 inbuffer[9]）就被认为是一个合法的 C 字符串。
    void Start()
    {
        _isrunning = true;
        char inbuffer[1024];
        while (_isrunning)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (n > 0)
            {
                InetAddr addr(peer);
                inbuffer[n] = 0;
                // 一个一个的单词
                std::cout << "[" << addr.Ip() << ":" << addr.Port() << "]#" << inbuffer << std::endl;
                std::string result = _func(inbuffer);
                sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr *)&peer, len);
            }
            else
            {
                std::cout << "recvfrom, error" << std::endl;
            }
        }
    }
    ~UdpServer()
    {
        if (_sockfd > gsockfd)
            ::close(_sockfd);
    }

private:
    int _sockfd;
    uint16_t _localport;
    bool _isrunning;
    func_t _func;
};

// 1. using 关键字
// using 是 C++11 引入的关键字，用于定义类型别名。它与传统的 typedef 类似，但更现代、更简洁。using 可以为复杂的类型提供更直观、更易读的别名。
// 例如，using 可以让我们更方便地为模板类型定义别名，这在某些情况下比 typedef 更易于理解。
// 这种写法在 C++11 和之后的标准中更常见，特别是在模板类型、函数类型等复杂类型定义时。

// 2. std::function<std::string(std::string)>
// std::function 是 C++11 标准库提供的一个模板类，它用于封装任何可调用对象（例如函数指针、函数对象、Lambda 表达式、成员函数指针等），使它们能够被统一处理。
// std::function 是一个通用的函数包装器，它提供了统一的接口来调用不同类型的可调用对象。具体来说，std::function 可以封装函数、函数指针、Lambda 表达式、绑定函数、成员函数等。
// std::function<std::string(std::string)> 表示一个接受 std::string 类型参数并返回 std::string 类型结果的可调用对象。
// 解释一下 std::function<std::string(std::string)>：
// std::string：表示返回类型，即被封装的函数调用时会返回一个 std::string 类型的结果。
// std::string(std::string)：表示封装的函数类型，即该函数接受一个 std::string 类型的参数，返回一个 std::string 类型的值。
// 简单来说，std::function<std::string(std::string)> 是一个可以封装任何接受 std::string 参数并返回 std::string 的可调用对象。

// 3. func_t 类型别名
// func_t 是通过 using 定义的类型别名，它代表了一个类型为 std::function<std::string(std::string)> 的函数对象。
// 也就是说，func_t 类型可以表示：
// 普通函数
// Lambda 表达式
// 函数对象（即重载了 operator() 的对象）
// 函数指针等
// 这些可调用对象的共同特点是，它们都能够接收一个 std::string 类型的参数，并返回一个 std::string 类型的值。

// 这段代码实现了一个简单的 UDP 服务器类 UdpServer，能够接收客户端发送的消息，并通过用户提供的回调函数处理消息后返回响应。
// 该类还实现了日志记录、套接字管理、地址绑定等基础的网络通信功能。下面详细分析代码的各个部分：

// 全局常量与类型定义
// gsockfd：默认的套接字文件描述符，设置为 -1 作为无效值。
// glocalport：默认的本地端口号，设置为 8888，如果未传递特定端口号，默认使用此端口。
// 错误码枚举：定义了 SOCKET_ERROR 和 BIND_ERROR，用于表示套接字创建和绑定失败的错误。

// func_t 类型别名
// func_t：定义了一个类型别名 func_t，表示一个回调函数类型。该函数接收一个 std::string 类型的参数，并返回一个 std::string 类型的结果。通过这个回调函数，服务器可以自定义处理接收到的数据。

// UdpServer 类
// UdpServer 类是实现 UDP 服务器功能的核心部分。它继承自 nocopy，意味着不能拷贝该类的实例。
// _sockfd：存储套接字文件描述符，用于进行网络通信。
// _localport：服务器监听的本地端口号。
// _isrunning：标志服务器是否正在运行。
// _func：存储一个回调函数，用于处理接收到的数据。

// 构造函数
// func：构造函数接受一个回调函数 func，用于处理接收到的消息。
// localport：设置监听端口，如果没有提供，默认使用 glocalport（8888）。

// InitServer 方法
// 创建套接字：使用 ::socket() 创建一个 UDP 套接字。AF_INET 表示 IPv4 地址族，SOCK_DGRAM 表示 UDP 类型。
// 如果创建失败，记录错误日志并退出。
// 绑定套接字：::bind() 将套接字与指定的本地端口绑定。
// local.sin_family = AF_INET：表示使用 IPv4 地址。
// local.sin_port = htons(_localport)：设置本地端口号（使用 htons() 转换为网络字节序）。
// local.sin_addr.s_addr = INADDR_ANY：绑定到所有本地网络接口的 IP 地址。

// Start 方法
// 循环接收消息：recvfrom() 从套接字接收数据，并将数据存储在 inbuffer 中。该方法会阻塞，直到接收到数据。
// peer：存储客户端的地址信息（IP 和端口）。
// len：保存 peer 地址的大小。
// n：接收到的字节数。
// 如果接收到数据，则调用传入的回调函数 _func 对数据进行处理，并通过 sendto() 发送回响应数据。
// 处理异常：如果 recvfrom() 返回错误（n <= 0），则输出错误信息。

// 析构函数
// 关闭套接字：当服务器停止运行时，关闭套接字，释放资源。